package puzzle.nurikabe.solver;

import java.util.HashMap;
import java.util.Map;

import puzzle.common.data.HatanException;
import puzzle.common.point.Direction;
import puzzle.common.solver.TopologicalRule;
import puzzle.common.solver.topology.ClosureAnalyzable;
import puzzle.common.solver.topology.ClosureAnalyzer;
import puzzle.nurikabe.data.ShimaState;
import puzzle.nurikabe.data.NurikabeBoard;
import puzzle.nurikabe.data.StateUtil;

/**
 * 数字の入らないシマが壁に囲まれてはいけないというルール
 */
class NeutralClosureRule implements TopologicalRule<NurikabeBoard, NurikabeTopology> {
	public boolean apply(final NurikabeBoard b, NurikabeTopology topology) throws HatanException {
		ClosureAnalyzable g = new ClosureAnalyzable() {
			public int getHeight() {
				return b.height;
			}

			public int getWidth() {
				return b.width;
			}

			public boolean isConnected(int x, int y, Direction d) {
				return b.getBoardState(x, y) >= 0 && b.getBoardState(x + d.v.dx, y + d.v.dy) >= 0;
			}

			public boolean isClosureMember(int x, int y) {
				return true;
			}
		};

		ClosureAnalyzer z = new ClosureAnalyzer(g);

		Map<Integer, Boolean> numberExistsMap = new HashMap<Integer, Boolean>();
		for (int y = 0; y < b.height; y++) {
			for (int x = 0; x < b.width; x++) {
				if (!StateUtil.isRealKabe(b.getBoardState(x, y))) {
					int n = z.getClosureNumber(x, y);
					if (!numberExistsMap.containsKey(n)) {
						numberExistsMap.put(n, false);
					}
				}
			}
		}

		for (ShimaState s : b.getShimaStates()) {
			int n = z.getClosureNumber(s.p.x, s.p.y);
			numberExistsMap.put(n, true);
		}

		boolean isChanged = false;
		for (Map.Entry<Integer, Boolean> entry : numberExistsMap.entrySet()) {
			if (!entry.getValue()) {
				isChanged |= setKabeOnClosure(b, z, entry.getKey());
			}
		}
		return isChanged;
	}

	private boolean setKabeOnClosure(NurikabeBoard b, ClosureAnalyzer z, int n)
			throws HatanException {
		boolean isChanged = false;
		for (int y = 0; y < b.height; y++) {
			for (int x = 0; x < b.width; x++) {
				if (z.getClosureNumber(x, y) == n) {
					isChanged |= b.setKabe(x, y);
				}
			}
		}
		return isChanged;
	}
}
